/**
 * 
 */
package com.feib.stms.batch.service;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Service;

import com.feib.stms.batch.domain.MQHostInfo;

import com.ibm.mq.MQC;
import com.ibm.mq.MQEnvironment;
import com.ibm.mq.MQException;
import com.ibm.mq.MQGetMessageOptions;
import com.ibm.mq.MQMessage;
import com.ibm.mq.MQPutMessageOptions;
import com.ibm.mq.MQQueue;
import com.ibm.mq.MQQueueManager;

/**
 * @author Jimmy Liu
 *
 */
@Service("mqService")
//@Scope(value=BeanDefinition.SCOPE_PROTOTYPE)
public class MQService implements Serializable 
{
	private static final long serialVersionUID = 1342286161117945263L;

	protected Logger logger = LoggerFactory.getLogger(this.getClass());

	public static final String DEFAULT_MQ_HOST_NAME = "CSF_NONEXML";

	/** MQ RF 訊息格式設定的常數，Class MQMessage所用 */
	public static final String MQFMT_RF_HEADER_2 = "MQHRF2  ";

	/** MQ RF 訊息內容的Header 設定 */
	public static final byte[] MQRFH_STRUC_ID = { 'R', 'F', 'H', ' ' };

	/** MQ RF 訊息內容的Header 設定 */
	public static final int MQRFH_VERSION_2 = 2;

	/** MQ RF 訊息內容的Header 設定 */
	public static final int MQRFH_STRUC_LENGTH_FIXED_2 = 40;

	/** MQ RF 訊息內容的Header 設定 */
	public static final byte[] MQFMT_STRING_ARRAY = { 'M', 'Q', 'S', 'T', 'R', ' ', ' ', ' ' };

	/** MQ RF 訊息內容的Header 設定 */
	public static final int MQRFH_NONE = 0x00000000;

	/** 電文收送型態--XML格式 */
	public static char MSG_TYPE_XML = 'X';

	/** 電文收送型態--RHF2格式 */
	public static char MSG_TYPE_NONEXML = 'R';

	/** MQ 主機名稱 或 ip */
	private static String MQhostname;

	/** MQ 主機TCP PORT */
	private static int MQport;

	/** MQ 連線連線狀態 0:使用折返回訊息 1:訊息送MQ主機 */
	public static String MQonline;

	/** MQ 連線至後端主機資訊 */
	private static HashMap<String, MQHostInfo> HostInfo;

	/** MQ 折返TOTA訊息pool */
	private static HashMap<String, List<String>> FAKE_TOTA_POOL;

	/** 初始化設定值狀態 */
	private static boolean isOK_Info = false;

	/** 設定檔工作目錄 */
	private static String MQ_WORK_HOME;

	@Resource(name="mqProperties")
	private Properties mqProperties;


	/**
	 * 模擬的回傳電文
	 */
	private List<String> myFakeTota;



	private int myFakeIdx, myExprityDay, myWaitIntervalDay;
	private String myMQmanager, myMQchannel, myRequestQueue, myResponseQueue, myRFHeader;

	private String myTITA;

	private String myMessageID;


	@PostConstruct
	public void init() throws Exception
	{
		loadProperties();
		setMyHostInfo(DEFAULT_MQ_HOST_NAME);
	}

	public boolean loadProperties()
	{
		if (isOK_Info == true)
			return true;

		try {
			logger.info("Init MQ configure starting...");
			MQ_WORK_HOME = mqProperties.getProperty("MQ_WORK_HOME");
			logger.info("MQWORKHOME={}", MQ_WORK_HOME);
			MQhostname = mqProperties.getProperty("MQhostname");
			logger.info("MQhostname={}", MQhostname);
			MQport = Integer.parseInt(mqProperties.getProperty("MQport"));
			logger.info("MQport=" + MQport);
			MQonline = mqProperties.getProperty("MQonline");
			logger.info("MQonline={}", MQonline);

			HostInfo = new HashMap<String, MQHostInfo>();
			int idx = 1;

			String[] tmp = null;
			int propertiesLength = 10;
			MQHostInfo mqHostInfo = null;

			while (true)
			{				
				tmp = new String[propertiesLength];
				tmp[0] = mqProperties.getProperty(idx + "#Host");               
				tmp[1] = mqProperties.getProperty(idx + "#MQmanager");
				tmp[2] = mqProperties.getProperty(idx + "#MQchannel");
				tmp[3] = mqProperties.getProperty(idx + "#RequestQueue");
				tmp[4] = mqProperties.getProperty(idx + "#ResponseQueue");
				tmp[5] = mqProperties.getProperty(idx + "#ExprityDay");
				tmp[6] = mqProperties.getProperty(idx + "#WaitIntervalDay");
				/* 全長要是2、4的能整除 */
				tmp[7] = mqProperties.getProperty(idx + "#RFHeader");
				tmp[8] = mqProperties.getProperty(idx + "#MessageID");
				tmp[9] = mqProperties.getProperty(idx + "#TITA");

				if (tmp[0] != null){
					logger.info(idx + "#Host={}", tmp[0]);
					logger.info(idx + "#MQmanager={}", tmp[1]);
					logger.info(idx + "#MQchannel={}", tmp[2]);
					logger.info(idx + "#RequestQueue={}", tmp[3]);
					logger.info(idx + "#ResponseQueue={}", tmp[4]);
					logger.info(idx + "#ExprityDay={}", tmp[5]);
					logger.info(idx + "#WaitIntervalDay={}", tmp[6]);
					/* 全長要是2、4的能整除 */
					logger.info(idx + "#RFHeader={}", tmp[7]);
					logger.info(idx + "#MessageID={}", tmp[8]);
					logger.info(idx + "#TITA={}", tmp[9]);
				}

				int cntAllNull = 0;
				int cntPartNull = 0;
				for (int i = 0; i < tmp.length; i++)
				{
					if (tmp[i] == null)
						cntAllNull += 1;
					if (tmp[i] == null || tmp[i].length() == 0)
						cntPartNull += 1;
				}

				if (cntAllNull == tmp.length)
				{
					idx++;
					for(int i=0; i< propertiesLength; i++) // free resource
					tmp[i] = null;
					break;
				}

				if (cntPartNull > 2)
				{
					idx++;
					for(int i=0; i< propertiesLength; i++) // free resource
						tmp[i] = null;
					continue;
				}

				try
				{
					Integer.parseInt(tmp[5]);
					Integer.parseInt(tmp[6]);
				}
				catch (Exception ex)
				{
					idx++;
					for(int i=0; i< propertiesLength; i++) // free resource
					tmp[i] = null;
					continue;
				}




				mqHostInfo = new MQHostInfo();

				mqHostInfo.setHost(tmp[0]);
				mqHostInfo.setMqmanager(tmp[1]);
				mqHostInfo.setMqchannel(tmp[2]);
				mqHostInfo.setRequestQueue(tmp[3]);
				mqHostInfo.setResponseQueue(tmp[4]);
				mqHostInfo.setExprityDay(Integer.parseInt(tmp[5]));
				mqHostInfo.setWaitIntervalDay(Integer.parseInt(tmp[6]));
				/* 全長要是2、4的能整除 */
				mqHostInfo.setRfHeader(tmp[7]);
				mqHostInfo.setMessageID(tmp[8]);
				mqHostInfo.setTita(tmp[9]);


				if (StringUtils.isNotEmpty(mqHostInfo.getHost())){
					logger.info(idx + "#Host={}", mqHostInfo.getHost());
					logger.info(idx + "#MQmanager={}", mqHostInfo.getMqmanager());
					logger.info(idx + "#MQchannel={}", mqHostInfo.getMqchannel());
					logger.info(idx + "#RequestQueue={}", mqHostInfo.getRequestQueue());
					logger.info(idx + "#ResponseQueue={}", mqHostInfo.getResponseQueue());
					logger.info(idx + "#ExprityDay={}", mqHostInfo.getExprityDay());
					logger.info(idx + "#WaitIntervalDay={}", mqHostInfo.getWaitIntervalDay());
					/* 全長要是2、4的能整除 */
					logger.info(idx + "#RFHeader={}", mqHostInfo.getRfHeader());
					logger.info(idx + "#MessageID={}", mqHostInfo.getMessageID());
					logger.info(idx + "#TITA={}", mqHostInfo.getTita());
				}


				HostInfo.put(mqHostInfo.getHost(), mqHostInfo);
				mqHostInfo = null; // free resource
				for(int i=0; i< propertiesLength; i++) // free resource
					tmp[i] = null;
				idx++;
			}



			// 讀取折返電文檔(TOTA)資料
			FAKE_TOTA_POOL = new HashMap<String, List<String>>();
			File xfile = new File(MQ_WORK_HOME + "/totapool");
			List<String> totaData;
			if (xfile.exists() && xfile.isDirectory())
			{
				String[] key = xfile.list();
				for (int i = 0; i < key.length; i++)
				{
					xfile = new File(MQ_WORK_HOME + "/totapool/" + key[i]);
					if (!xfile.isFile())
						continue;

					totaData = new ArrayList<String>();

					String tempStr = null;
					BufferedReader in2 = null;
					try {
						in2 = new BufferedReader(new FileReader(xfile.getAbsoluteFile()));
						while ((tempStr = in2.readLine()) != null)
						{               
							if (logger.isDebugEnabled())
								logger.debug("{}=[{}]", key[i], tempStr);
							totaData.add(tempStr);
						}               
						FAKE_TOTA_POOL.put(key[i], totaData);

					}
					finally
					{
						try {
							if (null != in2)
								in2.close();
						}
						catch (Exception e2) {
							// do nothing.
						}
					}
				}
			}

			isOK_Info = true;
			logger.info("Init MQ configure end.");
		}
		catch (Exception e) {
			logger.error("MQ 起始錯誤！", e);
			isOK_Info = false;
		}

		return isOK_Info;
	}

	/**
	 * 
	 * @param host
	 * @throws Exception
	 */
	public void setMyHostInfo(String host) throws Exception
	{
		MQHostInfo myHostInfo = HostInfo.get(host);

		if (myHostInfo == null)
			throw new IllegalArgumentException("不合法的 host");

		this.myMQmanager = myHostInfo.getMqmanager();
		this.myMQchannel = myHostInfo.getMqchannel();
		this.myRequestQueue = myHostInfo.getRequestQueue();
		this.myResponseQueue = myHostInfo.getResponseQueue();
		this.myExprityDay = myHostInfo.getExprityDay().intValue();
		this.myWaitIntervalDay = myHostInfo.getWaitIntervalDay().intValue();
		this.myRFHeader = myHostInfo.getRfHeader();
		this.myMessageID = myHostInfo.getMessageID();
		this.myTITA = myHostInfo.getTita();

		MQEnvironment.hostname = MQhostname;
		MQEnvironment.port = MQport;
		MQEnvironment.channel = myMQchannel;
		MQEnvironment.CCSID = 950;
		MQEnvironment.userID = "";
		MQEnvironment.password = "";

	}



	/**
	 * 將電文字串傳送至後端主機
	 * @param tita
	 * @param msgType
	 * @param msgId
	 * @param msgName
	 * @throws Exception
	 */
	public void send(String tita, char msgType, String msgId, String msgName) throws Exception
	{
		if (!loadProperties())
			return ;

		MQQueueManager qManager = null;
		MQQueue requestQueue = null;

		try
		{
			if (!(msgType == MSG_TYPE_XML || msgType == MSG_TYPE_NONEXML))
				throw new IllegalArgumentException("不合法的 msgType");

			if (!isDigit(msgId) || msgId.length() != 10)
				throw new IllegalArgumentException("不合法的 msgId");

			msgId = msgId + "              ";

			logger.info("#MSGID={}", msgId);
			logger.info("#TITA={}", tita);

			if ("0".equals(MQonline))
			{
				this.myFakeIdx = 0;
				myFakeTota = FAKE_TOTA_POOL.get(msgName);
				return;
			}
			else if (msgType == MSG_TYPE_XML)
			{
				qManager = new MQQueueManager(myMQmanager);

				int openOptions = MQC.MQOO_OUTPUT;
				requestQueue = qManager.accessQueue(myRequestQueue, openOptions, null, null, null);

				MQMessage requestMsg = new MQMessage();
				requestMsg.characterSet = 950;
				requestMsg.messageId = msgId.getBytes();
				requestMsg.writeString(tita);
				requestMsg.expiry = myExprityDay;

				MQPutMessageOptions requestPmo = new MQPutMessageOptions();

				requestQueue.put(requestMsg, requestPmo);
			}
			else
			{
				qManager = new MQQueueManager(myMQmanager);

				int req_openOptions = MQC.MQOO_OUTPUT | MQC.MQOO_BIND_NOT_FIXED;
				requestQueue = qManager.accessQueue(myRequestQueue, req_openOptions, null, null, null);

				MQMessage requestMsg = new MQMessage();
				requestMsg.characterSet = 950;
				requestMsg.format = MQFMT_RF_HEADER_2;
				requestMsg.messageId = msgId.getBytes();
				requestMsg.replyToQueueName = myRequestQueue;
				requestMsg.replyToQueueManagerName = myMQmanager;
				requestMsg.persistence = MQC.MQPER_PERSISTENT;
				requestMsg.expiry = myExprityDay;
				/** **************************************************************** */
				/* Imbed an RF Header into the requestMsg stream */
				/** **************************************************************** */
				requestMsg.write(MQRFH_STRUC_ID); // StructId
				requestMsg.writeInt(MQRFH_VERSION_2); // RFH Version
				requestMsg.writeInt(MQRFH_STRUC_LENGTH_FIXED_2 + myRFHeader.length());
				requestMsg.writeInt(MQC.MQENC_NATIVE); // Encoding
				requestMsg.writeInt(MQC.MQCCSI_INHERIT); // qmgr CCSID
				requestMsg.write(MQFMT_STRING_ARRAY); // Format
				requestMsg.writeInt(MQRFH_NONE); // Flags
				requestMsg.writeInt(1208); // NameValueCCSID
				requestMsg.writeInt(myRFHeader.length()); // NameValueLength
				/** **************************************************************** */
				/* Append the name/values pairs to the message stream */
				/** **************************************************************** */
				requestMsg.writeBytes(myRFHeader);
				requestMsg.writeString(tita);

				MQPutMessageOptions requestPmo = new MQPutMessageOptions();

				requestQueue.put(requestMsg, requestPmo);
			}      

		}
		catch (MQException ex)
		{
			logger.error("電文發送失敗", ex);
			throw ex;
		}
		catch (Exception ex)
		{
			logger.error("電文發送失敗", ex);
			throw ex;
		}
		finally
		{
			if (requestQueue != null && requestQueue.isOpen())
				requestQueue.close();
			if (qManager != null && qManager.isConnected())
				qManager.disconnect();
		}

	}

	/**
	 * 收取主機回覆的電文字串
	 * @param msgId
	 * @return
	 * @throws Exception
	 */
	public String rcv(String msgId) throws Exception
	{
		MQQueueManager qManager = null;
		MQQueue responseQueue = null;

		String totaStr = null;
		try
		{
			if (!isDigit(msgId) || msgId.length() != 10)
				throw new IllegalArgumentException("不合法的 msgId");

			msgId = msgId + "              ";

			if ("0".equals(MQonline))
			{       
				if (myFakeIdx > (myFakeTota.size() - 1))
					return null;
				String tmpStr = (String) myFakeTota.get(myFakeIdx);             
				myFakeIdx++;            
				totaStr = tmpStr;
				logger.info(" #TOTA={}", totaStr);
				return totaStr;
			}

			qManager = new MQQueueManager(myMQmanager);

			int openOptions = MQC.MQOO_INPUT_AS_Q_DEF;
			responseQueue = qManager.accessQueue(myResponseQueue, openOptions, null, null, null);

			MQMessage responseMsg = new MQMessage();
			responseMsg.characterSet = 950;
			responseMsg.format = MQC.MQFMT_STRING;
			responseMsg.messageId = msgId.getBytes();

			MQGetMessageOptions responseGmo = new MQGetMessageOptions();
			responseGmo.options = MQC.MQGMO_WAIT;
			responseGmo.waitInterval = myWaitIntervalDay;

			responseQueue.get(responseMsg, responseGmo);

			byte[] totabyte = new byte[responseMsg.getMessageLength()];
			responseMsg.readFully(totabyte, 0, responseMsg.getMessageLength());
			totaStr = new String(totabyte);

			logger.info(" #MSGID={}", msgId); 
			logger.info(" #TOTA={}", totaStr);
		}
		catch (MQException ex)
		{
			logger.error(ex.toString(), ex);
			throw ex;
		}
		catch (Exception ex)
		{
			logger.error(ex.toString(), ex);
			throw ex;
		}
		finally
		{
			if (!"0".equals(MQonline))
			{   
				if (responseQueue != null && responseQueue.isOpen())
					responseQueue.close();
				if (qManager.isConnected())
					qManager.disconnect();
			}
		}
		return totaStr;
	}

	private boolean isDigit(String str) throws Exception
	{
		try
		{
			for (int i = 0; i < str.length(); i++)
			{
				if (!Character.isDigit(str.charAt(i)))
					throw new NumberFormatException();
			}
			return true;
		}
		catch (Exception ex)
		{
			return false;
		}
	}

}
